import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
from ttkthemes import ThemedTk
import os
import time
import threading
import datetime
import pystray
from PIL import Image, ImageDraw
import subprocess
import platform
import sys
import ctypes

alarms = []

class MemoWindow(tk.Toplevel):
    def __init__(self, master, get_memo_text):
        super().__init__(master)
        self.title("闹铃备忘录")
        self.geometry("360x440+100+100")
        self.resizable(False, True)
        self.overrideredirect(True)  # 去掉系统标题栏
        self.configure(bg="#e6ecf5")
        self.get_memo_text = get_memo_text
        # 自定义标题栏
        self.titlebar = tk.Frame(self, bg="#e6ecf5", relief='flat', height=32)
        self.titlebar.pack(fill='x', side='top')
        self.title_label = tk.Label(self.titlebar, text="闹铃备忘录", bg="#e6ecf5", fg="#222", font=("Microsoft YaHei", 12, "bold"))
        self.title_label.pack(side='left', padx=12, pady=4)
        self.close_btn = tk.Button(self.titlebar, text="✕", bg="#e6ecf5", fg="#666", bd=0, font=("Segoe UI", 12), activebackground="#e6ecf5", activeforeground="#e81123", command=self.hide, cursor="hand2")
        self.close_btn.pack(side='right', padx=8, pady=4)
        self.titlebar.bind('<ButtonPress-1>', self.start_move)
        self.titlebar.bind('<B1-Motion>', self.do_move)
        self.title_label.bind('<ButtonPress-1>', self.start_move)
        self.title_label.bind('<B1-Motion>', self.do_move)
        # 内容区
        frm = tk.Frame(self, bg="#e6ecf5")
        frm.pack(fill='both', expand=True, padx=10, pady=(0,10))
        self.text = tk.Text(frm, height=22, font=("Microsoft YaHei", 12), bg="#f8fbff", fg="#222", relief='flat', bd=0, wrap='word', highlightthickness=0)
        self.text.pack(fill='both', expand=True, padx=6, pady=6)
        self.text.config(state='disabled')
        self.text.bind("<FocusIn>", lambda e: self.text.config(insertbackground="#2563eb"))
        self.after(500, self.sync)
        self.protocol('WM_DELETE_WINDOW', self.hide)
        self.enable_acrylic()
    def start_move(self, event):
        self._x = event.x
        self._y = event.y
    def do_move(self, event):
        x = self.winfo_pointerx() - self._x
        y = self.winfo_pointery() - self._y
        self.geometry(f"+{x}+{y}")
    def sync(self):
        self.text.config(state='normal')
        self.text.delete(1.0, tk.END)
        self.text.insert(tk.END, self.get_memo_text())
        self.text.config(state='disabled')
        self.after(1000, self.sync)
    def hide(self):
        self.withdraw()
    def show(self):
        self.deiconify()
        self.lift()
    def enable_acrylic(self):
        # 仅在Win10/11下尝试Acrylic
        if sys.platform != 'win32':
            self.attributes('-alpha', 0.88)
            return
        try:
            hwnd = ctypes.windll.user32.GetParent(self.winfo_id())
            accent = ctypes.c_int(4)  # ACCENT_ENABLE_ACRYLICBLURBEHIND = 4
            accent_state = ctypes.c_int(0x20 | 0x40)  # ACCENT_ENABLE_ACRYLICBLURBEHIND | ACCENT_ENABLE_BLURBEHIND
            accent_flags = ctypes.c_int(2)  # DWM_ACCENT_ENABLE_BLURBEHIND
            class ACCENTPOLICY(ctypes.Structure):
                _fields_ = [
                    ("AccentState", ctypes.c_int),
                    ("AccentFlags", ctypes.c_int),
                    ("GradientColor", ctypes.c_int),
                    ("AnimationId", ctypes.c_int)
                ]
            class WINCOMPATTRDATA(ctypes.Structure):
                _fields_ = [
                    ("Attribute", ctypes.c_int),
                    ("Data", ctypes.c_void_p),
                    ("SizeOfData", ctypes.c_size_t)
                ]
            policy = ACCENTPOLICY()
            policy.AccentState = 4  # ACCENT_ENABLE_ACRYLICBLURBEHIND
            policy.GradientColor = 0xDDE6ECF5  # ARGB (DD透明度, E6ECF5淡蓝灰)
            policy.AccentFlags = 2
            data = WINCOMPATTRDATA()
            data.Attribute = 19  # WCA_ACCENT_POLICY
            data.Data = ctypes.addressof(policy)
            data.SizeOfData = ctypes.sizeof(policy)
            ctypes.windll.user32.SetWindowCompositionAttribute(hwnd, ctypes.byref(data))
        except Exception:
            self.attributes('-alpha', 0.88)

class AllInOneApp:
    def __init__(self):
        # 检测系统版本，选择主题
        win_ver = platform.version()
        win_major = int(platform.release()) if platform.release().isdigit() else 10
        if win_major >= 10:
            theme = "arc"
        else:
            theme = "plastik"
        self.root = ThemedTk(theme=theme)
        self.root.title("多功能桌面助手（闹钟/定时/秒表）")
        self.root.geometry("480x540")
        self.root.resizable(False, False)
        self.root.configure(bg="#f3f6fa")
        self.set_style()
        self.setup_tray()
        self.notebook = ttk.Notebook(self.root)
        self.notebook.pack(expand=True, fill='both', padx=10, pady=10)
        self.memo_window = MemoWindow(self.root, self.get_memo_text)
        self.setup_alarm_page()
        self.memo_window.show()
        self.setup_shutdown_page()
        self.setup_script_page()
        self.setup_timer_page()
        self.root.protocol('WM_DELETE_WINDOW', self.minimize_to_tray)
        threading.Thread(target=self.alarm_thread, daemon=True).start()

    def set_style(self):
        # 字体兼容处理
        try:
            font_name = "Segoe UI"
            self.root.option_add("*Font", (font_name, 11))
        except Exception:
            font_name = "TkDefaultFont"
        style = ttk.Style(self.root)
        style.configure("TButton", font=(font_name, 11), padding=10, relief="flat", borderwidth=0)
        style.configure("TLabel", font=(font_name, 11), background="#f3f6fa", padding=6)
        style.configure("TEntry", font=(font_name, 11), padding=6)
        style.configure("TNotebook.Tab", font=(font_name, 11, "bold"), padding=[16, 8])
        style.configure("TFrame", background="#f3f6fa")
        style.configure("TLabelframe", background="#f3f6fa", padding=10)
        style.configure("TLabelframe.Label", font=(font_name, 11, "bold"), padding=6)
        style.map("TButton",
                  foreground=[('pressed', '#fff'), ('active', '#2563eb')],
                  background=[('pressed', '#2563eb'), ('active', '#e0e7ef')])

    def setup_tray(self):
        image = Image.new('RGB', (64, 64), color=(243, 246, 250))
        d = ImageDraw.Draw(image)
        d.ellipse((16, 16, 48, 48), fill=(37, 99, 235))
        menu = (
            pystray.MenuItem('显示', self.show_window),
            pystray.MenuItem('显示备忘录', lambda icon, item: self.memo_window.show()),
            pystray.MenuItem('退出', self.quit_app)
        )
        self.tray_icon = pystray.Icon("allinone", image, "多功能助手", menu)
        threading.Thread(target=self.tray_icon.run, daemon=True).start()

    def minimize_to_tray(self):
        self.root.withdraw()

    def show_window(self, icon=None, item=None):
        self.root.deiconify()
        self.root.lift()

    def quit_app(self, icon=None, item=None):
        self.tray_icon.stop()
        self.root.quit()

    # 闹钟功能
    def setup_alarm_page(self):
        self.alarm_frame = ttk.Frame(self.notebook, padding=10)
        self.notebook.add(self.alarm_frame, text='闹钟')
        btn_frame = ttk.Frame(self.alarm_frame, padding=(0,0,0,8))
        btn_frame.pack(fill='x', pady=8)
        ttk.Button(btn_frame, text="添加闹钟", command=self.add_alarm).pack(side='left', padx=8)
        ttk.Button(btn_frame, text="删除选中闹钟", command=self.remove_alarm).pack(side='left', padx=8)
        ttk.Button(btn_frame, text="显示备忘录", command=self.memo_window.show).pack(side='left', padx=8)
        self.alarm_list = tk.Listbox(self.alarm_frame, font=("Segoe UI", 11), bd=0, highlightthickness=1, relief='flat', selectbackground="#2563eb", selectforeground="#fff")
        self.alarm_list.pack(fill=tk.BOTH, expand=True, padx=10, pady=8)
        self.update_alarm_list()

    def add_alarm(self):
        def on_ok():
            h = int(hour_var.get())
            m = int(min_var.get())
            time_str = f"{h:02d}:{m:02d}"
            dialog.destroy()
            msg = simpledialog.askstring("自定义内容", "请输入提示内容")
            if not msg: msg = "时间到了！"
            action = simpledialog.askstring("动作", "到点执行（无/关机/执行脚本）")
            script = ""
            if action == "执行脚本":
                script = simpledialog.askstring("脚本路径", "请输入要执行的脚本路径")
            alarms.append({'time': time_str, 'msg': msg, 'triggered': False, 'action': action, 'script': script})
            self.update_alarm_list()
        dialog = tk.Toplevel(self.root)
        dialog.title("选择闹钟时间")
        dialog.geometry("320x170")
        dialog.resizable(False, False)
        dialog.grab_set()
        # grid布局
        frm = ttk.Frame(dialog, padding=20)
        frm.pack(expand=True, fill='both')
        ttk.Label(frm, text="小时:").grid(row=0, column=0, padx=(0,10), pady=10, sticky='e')
        hour_var = tk.StringVar(value="08")
        hour_box = ttk.Combobox(frm, textvariable=hour_var, width=5, values=[f"{i:02d}" for i in range(0,24)], state="readonly", font=("Segoe UI", 11))
        hour_box.grid(row=0, column=1, padx=(0,20), pady=10)
        ttk.Label(frm, text="分钟:").grid(row=0, column=2, padx=(0,10), pady=10, sticky='e')
        min_var = tk.StringVar(value="00")
        min_box = ttk.Combobox(frm, textvariable=min_var, width=5, values=[f"{i:02d}" for i in range(0,60)], state="readonly", font=("Segoe UI", 11))
        min_box.grid(row=0, column=3, pady=10)
        ok_btn = ttk.Button(frm, text="确定", command=on_ok)
        ok_btn.grid(row=1, column=0, columnspan=4, pady=(18,0), ipadx=18, ipady=6)
        dialog.transient(self.root)
        dialog.wait_window()

    def remove_alarm(self):
        idx = self.alarm_list.curselection()
        if idx:
            alarms.pop(idx[0])
            self.update_alarm_list()

    def update_alarm_list(self):
        self.alarm_list.delete(0, tk.END)
        for alarm in alarms:
            self.alarm_list.insert(tk.END, f"{alarm['time']} - {alarm['msg']} - {alarm['action']}")
        # MemoWindow会自动同步，无需手动刷新

    def alarm_thread(self):
        while True:
            now = datetime.datetime.now().strftime("%H:%M")
            for alarm in alarms:
                if not alarm['triggered'] and alarm['time'] == now:
                    alarm['triggered'] = True
                    messagebox.showinfo("闹钟提醒", alarm['msg'])
                    self.play_alarm()
                    if alarm['action'] == "关机":
                        os.system("shutdown /s /t 1")
                    elif alarm['action'] == "执行脚本" and alarm['script']:
                        os.system(f"python {alarm['script']}")
            time.sleep(10)

    def play_alarm(self):
        try:
            from playsound import playsound
            playsound("alarm.mp3")
        except Exception:
            pass

    # 定时关机
    def setup_shutdown_page(self):
        self.shutdown_frame = ttk.Frame(self.notebook, padding=10)
        self.notebook.add(self.shutdown_frame, text='定时关机')
        ttk.Label(self.shutdown_frame, text="选择关机时间（分钟）:").pack(pady=12, anchor='w')
        self.shutdown_minutes = ttk.Entry(self.shutdown_frame)
        self.shutdown_minutes.pack(pady=8, padx=8, fill='x')
        self.shutdown_minutes.insert(0, "60")
        self.shutdown_button = ttk.Button(self.shutdown_frame, text="开始定时关机", command=self.start_shutdown_timer)
        self.shutdown_button.pack(pady=10, padx=8, fill='x')
        self.cancel_shutdown_button = ttk.Button(self.shutdown_frame, text="取消定时关机", command=self.cancel_shutdown_timer)
        self.cancel_shutdown_button.pack(pady=5, padx=8, fill='x')
        self.shutdown_label = ttk.Label(self.shutdown_frame, text="")
        self.shutdown_label.pack(pady=12, padx=8, anchor='w')
        self.shutdown_timer = None

    def start_shutdown_timer(self):
        try:
            minutes = int(self.shutdown_minutes.get())
            if minutes <= 0:
                raise ValueError
            if self.shutdown_timer:
                self.cancel_shutdown_timer()
            self.shutdown_end_time = time.time() + minutes * 60
            self.update_shutdown_timer()
            self.shutdown_button.state(['disabled'])
            messagebox.showinfo("提示", f"系统将在{minutes}分钟后关机")
        except ValueError:
            messagebox.showerror("错误", "请输入有效的分钟数")

    def update_shutdown_timer(self):
        if not hasattr(self, 'shutdown_end_time'):
            return
        remaining = self.shutdown_end_time - time.time()
        if remaining <= 0:
            self.execute_shutdown()
            return
        mins, secs = divmod(int(remaining), 60)
        self.shutdown_label.config(text=f"剩余时间: {mins:02d}:{secs:02d}")
        self.shutdown_timer = self.root.after(1000, self.update_shutdown_timer)

    def cancel_shutdown_timer(self):
        if self.shutdown_timer:
            self.root.after_cancel(self.shutdown_timer)
            self.shutdown_timer = None
            self.shutdown_label.config(text="")
            self.shutdown_button.state(['!disabled'])
            messagebox.showinfo("提示", "已取消定时关机")

    def execute_shutdown(self):
        self.shutdown_timer = None
        self.shutdown_label.config(text="")
        self.shutdown_button.state(['!disabled'])
        if messagebox.askyesno("确认", "是否确定要关机？"):
            os.system("shutdown /s /t 0")

    # 定时脚本
    def setup_script_page(self):
        self.script_frame = ttk.Frame(self.notebook, padding=10)
        self.notebook.add(self.script_frame, text='脚本执行')
        ttk.Label(self.script_frame, text="脚本路径:").pack(pady=12, anchor='w')
        self.script_path = ttk.Entry(self.script_frame, width=40)
        self.script_path.pack(pady=8, padx=8, fill='x')
        ttk.Label(self.script_frame, text="执行时间（分钟）:").pack(pady=12, anchor='w')
        self.script_minutes = ttk.Entry(self.script_frame)
        self.script_minutes.pack(pady=8, padx=8, fill='x')
        self.script_minutes.insert(0, "30")
        self.script_button = ttk.Button(self.script_frame, text="开始定时执行", command=self.start_script_timer)
        self.script_button.pack(pady=10, padx=8, fill='x')
        self.cancel_script_button = ttk.Button(self.script_frame, text="取消定时执行", command=self.cancel_script_timer)
        self.cancel_script_button.pack(pady=5, padx=8, fill='x')
        self.script_label = ttk.Label(self.script_frame, text="")
        self.script_label.pack(pady=12, padx=8, anchor='w')
        self.script_timer = None

    def start_script_timer(self):
        if not os.path.exists(self.script_path.get()):
            messagebox.showerror("错误", "脚本文件不存在")
            return
        try:
            minutes = int(self.script_minutes.get())
            if minutes <= 0:
                raise ValueError
            if self.script_timer:
                self.cancel_script_timer()
            self.script_end_time = time.time() + minutes * 60
            self.update_script_timer()
            self.script_button.state(['disabled'])
            messagebox.showinfo("提示", f"脚本将在{minutes}分钟后执行")
        except ValueError:
            messagebox.showerror("错误", "请输入有效的分钟数")

    def update_script_timer(self):
        if not hasattr(self, 'script_end_time'):
            return
        remaining = self.script_end_time - time.time()
        if remaining <= 0:
            self.execute_script()
            return
        mins, secs = divmod(int(remaining), 60)
        self.script_label.config(text=f"剩余时间: {mins:02d}:{secs:02d}")
        self.script_timer = self.root.after(1000, self.update_script_timer)

    def cancel_script_timer(self):
        if self.script_timer:
            self.root.after_cancel(self.script_timer)
            self.script_timer = None
            self.script_label.config(text="")
            self.script_button.state(['!disabled'])
            messagebox.showinfo("提示", "已取消定时执行")

    def execute_script(self):
        self.script_timer = None
        self.script_label.config(text="")
        self.script_button.state(['!disabled'])
        try:
            subprocess.run(['python', self.script_path.get()], check=True)
            messagebox.showinfo("提示", "脚本执行完成")
        except subprocess.CalledProcessError:
            messagebox.showerror("错误", "脚本执行失败")

    # 倒计时和秒表
    def setup_timer_page(self):
        self.timer_frame = ttk.Frame(self.notebook, padding=10)
        self.notebook.add(self.timer_frame, text='计时器')
        # 倒计时
        countdown_frame = ttk.LabelFrame(self.timer_frame, text="倒计时", padding=10)
        countdown_frame.pack(fill="x", padx=10, pady=12)
        ttk.Label(countdown_frame, text="时间（分钟）:").pack(side="left", padx=8)
        self.countdown_minutes = ttk.Entry(countdown_frame, width=10)
        self.countdown_minutes.pack(side="left", padx=8)
        self.countdown_minutes.insert(0, "25")
        self.countdown_button = ttk.Button(countdown_frame, text="开始", command=self.start_countdown)
        self.countdown_button.pack(side="left", padx=8)
        self.countdown_label = ttk.Label(countdown_frame, text="00:00")
        self.countdown_label.pack(side="left", padx=8)
        self.countdown_timer = None
        # 秒表
        stopwatch_frame = ttk.LabelFrame(self.timer_frame, text="秒表", padding=10)
        stopwatch_frame.pack(fill="x", padx=10, pady=12)
        self.stopwatch_button = ttk.Button(stopwatch_frame, text="开始", command=self.toggle_stopwatch)
        self.stopwatch_button.pack(side="left", padx=8)
        self.reset_button = ttk.Button(stopwatch_frame, text="重置", command=self.reset_stopwatch)
        self.reset_button.pack(side="left", padx=8)
        self.stopwatch_label = ttk.Label(stopwatch_frame, text="00:00:00")
        self.stopwatch_label.pack(side="left", padx=8)
        self.is_counting = False
        self.elapsed_time = 0
        self.stopwatch_timer = None

    def start_countdown(self):
        try:
            minutes = int(self.countdown_minutes.get())
            if minutes <= 0:
                raise ValueError
            if self.countdown_timer:
                self.root.after_cancel(self.countdown_timer)
            self.countdown_end_time = time.time() + minutes * 60
            self.update_countdown()
            self.countdown_button.config(text="停止", command=self.stop_countdown)
        except ValueError:
            messagebox.showerror("错误", "请输入有效的分钟数")

    def update_countdown(self):
        remaining = self.countdown_end_time - time.time()
        if remaining <= 0:
            self.stop_countdown()
            messagebox.showinfo("提示", "倒计时结束！")
            return
        mins, secs = divmod(int(remaining), 60)
        self.countdown_label.config(text=f"{mins:02d}:{secs:02d}")
        self.countdown_timer = self.root.after(1000, self.update_countdown)

    def stop_countdown(self):
        if self.countdown_timer:
            self.root.after_cancel(self.countdown_timer)
            self.countdown_timer = None
            self.countdown_label.config(text="00:00")
            self.countdown_button.config(text="开始", command=self.start_countdown)

    def toggle_stopwatch(self):
        if not self.is_counting:
            self.start_stopwatch()
        else:
            self.stop_stopwatch()

    def start_stopwatch(self):
        self.is_counting = True
        self.stopwatch_button.config(text="停止")
        self.update_stopwatch()

    def stop_stopwatch(self):
        self.is_counting = False
        self.stopwatch_button.config(text="开始")
        if self.stopwatch_timer:
            self.root.after_cancel(self.stopwatch_timer)

    def reset_stopwatch(self):
        self.stop_stopwatch()
        self.elapsed_time = 0
        self.stopwatch_label.config(text="00:00:00")

    def update_stopwatch(self):
        if self.is_counting:
            self.elapsed_time += 1
            hours, remainder = divmod(self.elapsed_time, 3600)
            mins, secs = divmod(remainder, 60)
            self.stopwatch_label.config(text=f"{hours:02d}:{mins:02d}:{secs:02d}")
            self.stopwatch_timer = self.root.after(1000, self.update_stopwatch)

    def get_memo_text(self):
        memo_lines = []
        for alarm in alarms:
            memo_lines.append(f"时间：{alarm['time']}\n内容：{alarm['msg']}\n动作：{alarm['action']}\n{'-'*22}")
        return '\n'.join(memo_lines)

    def run(self):
        self.root.mainloop()

if __name__ == "__main__":
    app = AllInOneApp()
    app.run() 